﻿using MetaSharp.Transformation.Patterns;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Threading.Tasks;

namespace MetaSharp.Transformation
{
	public abstract class Pattern
	{
        public PatternKind Kind { get; private set; }
        protected PatternMatch _compiled;

        public Pattern(PatternKind kind)
        {
            this.Kind = kind;
        }

        public PatternMatch ToPatternMatch()
        {
            lock(this)
            {
                if(_compiled == null)
                {
                    lock(this)
                    {
                        _compiled = this.Compile();
                    }
                }
            }

            return _compiled;
        }

        public abstract PatternMatch Compile();

        #region Match functions
        public static async Task<IMatch> Match(Pattern pattern, string input)
        {
            return await Match(pattern.Compile(), (IEnumerable)input);
        }

        public static async Task<IMatch> Match(Pattern pattern, params object[] input)
        {
            return await Match(pattern.Compile(), (IEnumerable)input);
        }

		public static async Task<IMatch> Match(PatternMatch pattern, IEnumerable input)
		{
            var s = new Scope(new Stream(input));
            var m = await pattern(s);
            return m;
		}
        #endregion

        #region Pattern factories
        public static Pattern And(params Pattern[] patterns)
		{
            return new AndPattern(patterns);
		}

		public static Pattern Or(params Pattern[] patterns)
		{
            return new OrPattern(patterns);
		}

		public static Pattern Any()
		{
            return new AnyPattern();
		}

		public static Pattern Default()
		{
            return new DefaultPattern();
		}

		public static Pattern Not(Pattern pattern)
		{
            return new NotPattern(pattern);
		}
        
		public static Pattern Value<T>(T value)
		{
            return new ValuePattern<T>(value);
		}

		public static Pattern Range<T>(T left, T right)
			where T : IComparable
		{
            return new RangePattern<T>(left, right);
		}

		public static Pattern Array()
		{
			return Array(Not(Any()));
		}

		public static Pattern Array(Pattern pattern)
		{
            return new ArrayPattern(pattern);
		}
        
		public static Pattern OneOrMore(Pattern pattern)
		{
            return new OneOrMorePattern(pattern);
		}
        
		public static Pattern ZeroOrMore(Pattern pattern)
		{
            return new ZeroOrMorePattern(pattern);
		}

        public static Pattern Optional(Pattern pattern)
        {
            return new Optional(pattern);
        }

        public static Pattern ExactQuantity(Pattern pattern, int quantity)
		{
            return new ExactQuantityPattern(pattern, quantity);
		}
        
        public static Pattern Production<TResult>(Pattern pattern, Func<TResult> production)
        {
            return Production(pattern, (Delegate)production);
        }

        public static Pattern Production<T, TResult>(Pattern pattern, Func<T, TResult> production)
        {
            return Production(pattern, (Delegate)production);
        }

        public static Pattern Production<T1, T2, TResult>(Pattern pattern, Func<T1, T2, TResult> production)
        {
            return Production(pattern, (Delegate)production);
        }

        public static Pattern Production(Pattern pattern, Delegate production)
        {
            return new ProductionPattern(pattern, production);
        }

        public static Pattern Variable(string name, Pattern pattern)
		{
            return new VariablePattern(name, pattern);
		}

		public static Pattern Switch(string name, Pattern pattern)
		{
            return new SwitchPattern(name, pattern);
		}

        public static Pattern Transform(params Pattern[] patterns)
        {
            return new TransformPattern(patterns);
        }

        public static Pattern Transform(IImmutableArray<Pattern> patterns)
        {
            return new TransformPattern(patterns);
        }
        
		public static Pattern Call(CallablePattern pattern, params Pattern[] parameters)
		{
            return new ClosurePattern(pattern, new ArrayList<Pattern>(parameters));
		}

		public static CallablePattern Closure(Func<IImmutableArray<Pattern>, Pattern> pattern)
		{
            return new CallablePattern(pattern);
		}
        
		public static Pattern Object(Type type, params PropertyPattern[] properties)
		{
            return new ObjectPattern(type, properties);
		}
        
		public static PropertyPattern Property(string name)
		{
			return new PropertyPattern(name, null);
		}

		public static PropertyPattern Property(string name, Pattern pattern)
		{
            return new PropertyPattern(name, pattern);
		}

        public static Pattern Reference(Func<Pattern> pattern)
        {
            return new ReferencePattern(pattern);
        }

		public static Pattern Rule(Pattern pattern, params MetadataAttribute[] attributes)
		{
            return new RulePattern(pattern, new ArrayList<MetadataAttribute>(attributes));
		}
        
		public static Pattern ErrorUnless(Pattern pattern)
		{
            return new ErrorUnlessPattern(pattern);
		}

		public static Pattern ErrorUntil(Pattern pattern)
		{
            return new ErrorUntilPattern(pattern);
		}
        #endregion
    }
}